﻿using Microsoft.Build.Evaluation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OD.ProjectCustomizationTool.Helpers
{
    internal sealed class ProjectMonitor : IDisposable, IProjectMonitor
    {
        #region Properties

        private static IDictionary<String, Project> MonitoredProjects = new Dictionary<String, Project>();

        private IList<IFlatFileUpdate> FlatFileUpdates;

        private IList<IEnvDteUpdate> EnvDteUpdates;

        public Project CurrentProject
        {
            get
            {
                if (!ProjectMonitor.MonitoredProjects.ContainsKey(this.Source.FullName))
                {
                    ProjectMonitor.MonitoredProjects.Add(this.Source.FullName, new Project(this.Source.FullName));
                }
                return ProjectMonitor.MonitoredProjects[this.Source.FullName];
            }
        }

        public ILogger Logger { get; private set; }

        public global::EnvDTE.Project Source { get; private set; }

        #endregion

        /// <summary>
        /// Instantiate a new monitor which observe a specific project.
        /// </summary>
        /// <param name="source">The project to monitor.</param>
        /// <param name="logger">The log mechanism.</param>
        public ProjectMonitor(global::EnvDTE.Project source, ILogger logger)
        {
            if (source == null)
            {   // This code has been generated from the 'argnull' custom snippet.
                throw new ArgumentNullException("source");
            }
            if (logger == null)
            {   // This code has been generated from the 'argnull' custom snippet.
                throw new ArgumentNullException("logger");
            }

            this.Source = source;
            this.Logger = logger;
            this.FlatFileUpdates = new List<IFlatFileUpdate>();
            this.EnvDteUpdates = new List<IEnvDteUpdate>();

            SourceControlHelper.CheckOut(this.CurrentProject.FullPath, this.Logger);
        }

        #region IDisposable Members

        // This code has been generated from the 'dispose' custom snippet.

        /// <summary>
        ///Track whether Dispose has been called. 
        /// </summary>
        private Boolean isDisposed; // = false;

        /// <summary>
        /// Implement IDisposable.
        /// Do not make this method virtual.
        /// A derived class should not be able to override this method.
        /// <see cref="ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.fr/fxref_mscorlib/html/d2fe234e-d59b-fa98-3f28-f6f3f75757e3.htm"/>
        /// <see cref="ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.fr/dv_vsetsa03/html/12afb1ea-3a17-4a3f-a1f0-fcdb853e2359.htm"/>
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose(bool disposing) executes in two distinct scenarios.
        /// If disposing equals true, the method has been called directly
        /// or indirectly by a user's code. Managed and unmanaged resources
        /// can be disposed.
        /// If disposing equals false, the method has been called by the
        /// runtime from inside the finalizer and you should not reference
        /// other objects. Only unmanaged resources can be disposed.
        /// </summary>
        private void Dispose(Boolean disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.isDisposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    this.CurrentProject.Save();

                    if (this.EnvDteUpdates.Count() > 0)
                    {
                        foreach (var item in this.EnvDteUpdates)
                        {
                            item.Update();
                        }
                        this.Source.Save();
                    }

                    foreach (var item in this.FlatFileUpdates)
                    {
                        item.Update();
                    }
                }

                // Note disposing has been done.
                this.isDisposed = true;
            }
        }

        #endregion

        /// <summary>
        /// Insantiate a new project tool of type <typeparamref name="T"/>, initialize and update it.
        /// </summary>
        /// <typeparam name="T">The project tool type.</typeparam>
        /// <returns>Returns the instantiated project tool.</returns>
        internal T Update<T>()
            where T : ProjectToolBase, new()
        {
            var projectTool = default(T);
            try
            {
                projectTool = new T();

                var envDteUpdate = projectTool as IEnvDteUpdate;
                if (envDteUpdate != null)
                {   // Register tools that needs EnvDTE usage.
                    this.EnvDteUpdates.Add(envDteUpdate);
                }

                var flatFileUpdate = projectTool as IFlatFileUpdate;
                if (flatFileUpdate != null)
                {   // Register tools that needs content file update.
                    this.FlatFileUpdates.Add(flatFileUpdate);
                }

                projectTool.Initialize(this);
                projectTool.Update();
            }
            catch (Exception ex)
            {
                this.Logger.WriteLine(ex.ToString());
            }
            return projectTool;
        }
    }
}
